#include "stdafx.h"
#include "Window3D.h"

#include "resource.h"

const DWORD STYLE = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

//-----------------------------------------------------------------------------
Window3D::Window3D(int w, int h, LPCTSTR caption, bool fs)
:	hWnd					(NULL),
	hDC					(NULL),
	hRC					(NULL),
	threadID				(0),
	threadHandle		(0),
	quitThread			(false),
	render				(NULL),
	fullscreen			(fs),
	handleMouseEvent	(NULL),
	handleKeyEvent		(NULL),
	references			(0)
{
//	Create A Window Class Structure
	WNDCLASSEX wc = {0};
	wc.hInstance			= GetModuleHandle(NULL);
	wc.lpszClassName		= TEXT("Window3D Class");

	static bool once(true);
	if(once)
	{
		once = false;
		wc.cbSize			= sizeof(wc);
		wc.style				= CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
		wc.lpfnWndProc		= WndProc;
		wc.hIcon				= (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 64, 64, 0);
//		wc.hIcon				= LoadIcon(0, IDI_APPLICATION);
		wc.hCursor			= NULL;
//		wc.hCursor			= LoadCursor(0, IDC_ARROW);

	//	Register Window Class
		RegisterClassEx(&wc);
	}

	RECT rect = {0,0,w,h};

	if(fullscreen)
	{
		rect.right	= GetSystemMetrics(SM_CXSCREEN);
		rect.bottom	= GetSystemMetrics(SM_CYSCREEN);
	}
	else
		AdjustWindowRect(&rect,STYLE,FALSE);

//	Create Window
	DWORD style = fullscreen ? WS_POPUP : STYLE;
	hWnd = CreateWindowEx(0, wc.lpszClassName, caption,
					style, CW_USEDEFAULT, CW_USEDEFAULT,
					rect.right-rect.left, rect.bottom-rect.top, NULL, NULL, wc.hInstance, NULL);

	if(hWnd==NULL)
		Error(TEXT("Couldn't create window."));
	else
		SetFocus(hWnd);

	InitializeCriticalSection(&refAccess);
}


//-----------------------------------------------------------------------------
Window3D::~Window3D()
{
//	exit message thread and/or wait for references
	while(threadHandle || references > 0)
	{
		quitThread = true;
		Sleep(1);
	}

//	free resources
	if(hRC)
	{
		wglMakeCurrent(NULL, NULL);
		wglDeleteContext(hRC);
	}

	if(hDC)
		ReleaseDC(hWnd, hDC);

	if(hWnd)
		DestroyWindow(hWnd);

	DeleteCriticalSection(&refAccess);
}

//-----------------------------------------------------------------------------
void Window3D::addReference()
{
	EnterCriticalSection(&refAccess);
	references++;
	LeaveCriticalSection(&refAccess);
}

//-----------------------------------------------------------------------------
void Window3D::removeReference()
{
	EnterCriticalSection(&refAccess);
	references--;
	LeaveCriticalSection(&refAccess);
}


//-----------------------------------------------------------------------------
bool Window3D::initialize()
{
	PIXELFORMATDESCRIPTOR pfd = {0};

	pfd.nSize		= sizeof(PIXELFORMATDESCRIPTOR);
	pfd.nVersion	= 1;
	pfd.dwFlags		= PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
	pfd.iPixelType	= PFD_TYPE_RGBA;
	pfd.cColorBits	= 32;
	pfd.cDepthBits	= 32;

	if(hDC == NULL)
		hDC = GetDC(hWnd);

	GLuint pf = ChoosePixelFormat(hDC, &pfd);

	if(pf)
	{
		PIXELFORMATDESCRIPTOR bestMatch_pfd;
		DescribePixelFormat(hDC, pf, sizeof(pfd), &bestMatch_pfd);

		if(!SetPixelFormat(hDC, pf, &pfd))
		{
			DWORD dwErrorCode = GetLastError();
			return false;
		}
	}
	else	return false;

	hRC = wglCreateContext(hDC);
	wglMakeCurrent(hDC, hRC);

	return true;
}

//-----------------------------------------------------------------------------
void Window3D::resize(int w, int h)
{
	RECT rect;
	GetWindowRect(hWnd, &rect);

	rect.right = rect.left + w;
	rect.bottom = rect.top + h;

	AdjustWindowRect(&rect,STYLE,FALSE);
	MoveWindow(hWnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, FALSE);
}

//-----------------------------------------------------------------------------
void Window3D::show(bool show)
{
//	Show or hide the window
	int cmd = show ? SW_SHOW : SW_HIDE;
	ShowWindow(hWnd,cmd);
}

//-----------------------------------------------------------------------------
void Window3D::switchFullscreen(bool fs)
{
	fullscreen = fs;
	int screenW = GetSystemMetrics(SM_CXSCREEN);
	int screenH = GetSystemMetrics(SM_CYSCREEN);
	DWORD style = fullscreen ? WS_POPUP : STYLE;

	if(hRC)
	{
		wglMakeCurrent(NULL, NULL);
		wglDeleteContext(hRC);
		hRC = NULL;
	}

	if(hDC)
	{
		ReleaseDC(hWnd, hDC);
		hDC = NULL;
	}

	SetWindowLongPtr(hWnd, GWL_STYLE, style);

	if(fullscreen)
	{
		MoveWindow(hWnd, 0, 0, screenW, screenH, FALSE);
		SetFocus(hWnd);
	}

	initialize();
}

//-----------------------------------------------------------------------------
LRESULT CALLBACK Window3D::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
	//----
	//	hide cursor inside our window
		case WM_SETCURSOR:
		{
			WORD hitTestCode = LOWORD(lParam);
			if(hitTestCode == HTCLIENT)
			{
				SetCursor(NULL);
				return 0;
			}
		}
		break;
/*		case WM_PAINT:
		case WM_SIZE:
		{
			int width  = LOWORD(lParam); 
			int height = HIWORD(lParam);
		}
		return 0; 
		break;
*/
		case WM_KEYDOWN:
		{
			DWORD key  = wParam;
			return 0;
		}
		break;

		case WM_CLOSE:
		case WM_DESTROY:
			PostQuitMessage(0);
		break;
	}

	return DefWindowProc(hWnd,message,wParam,lParam);
}



//-----------------------------------------------------------------------------
//	Window messages thread
DWORD WINAPI Window3D::WindowThread(LPVOID lpParam)
{
	Window3D **ptr = (Window3D**)lpParam;

	Window3D wnd;

	wnd.threadHandle	= GetCurrentThread();
	wnd.threadID		= GetCurrentThreadId();

	*ptr = &wnd;

	HWND hWnd = wnd.getHWND();
	MSG msg;

	while(wnd.quitThread == false)
	{
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD))
		{
			if(msg.message == WM_QUIT)
				break;

			bool handled = false;
			if(msg.hwnd == hWnd)
			{
				if			(wnd.handleMouseEvent && msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST)	handled = wnd.handleMouseEvent(msg);
				else if	(wnd.handleKeyEvent && msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST)			handled = wnd.handleKeyEvent(msg);
			//----
			//	doesn't work - why?
/*				if(msg.message == WM_SIZE)
				{
					wnd.clientW = LOWORD(msg.lParam);
					wnd.clientH = HIWORD(msg.lParam);
				}
*/			}

			if(!handled)
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}
		else
		{
			if(wnd.render)
			{
				wnd.render();
				SwapBuffers(wnd.hDC);
			}
			else
				Sleep(1);
		}
	}

	wnd.threadHandle = NULL;
	wnd.threadID = 0;

	return 0;
//	Here the destructor will be called, which waits for all references to be released.
}



//-----------------------------------------------------------------------------
Window3D *Window3D::CreateWindowAndThread()
{
	Window3D *ptr = NULL;

	CreateThread(NULL, 0, WindowThread, &ptr, 0, NULL);

//----
//	The thread will create the window and return 
	while(ptr==NULL)
		Sleep(1);

	return ptr;
}

